using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Reflection;
using ElteIk.RoboSoccer.Core;
using ElteIk.RoboSoccer.Core.Physics;
using SdlDotNet.Input;
using SdlDotNet.Audio;
using ElteIk.RoboSoccer.Graphics;

namespace ElteIk.RoboSoccer.GameGUI
{
    public partial class MainForm : Form
    {
        private Graphics.GraphicsEngine graphics;
        private Graphics.GdiGraphicsEngine gdiGraphics;
        private SDL.SdlGraphicsEngine sdlGraphics;
        private Core.RoboSoccerGame game;
        private Logger.Log log;

        private RobotPlayer selectedRobot;
        private int selectedRobotIndex = 0;

        private ElteIk.RoboSoccer.Communication.MultithreadedServer server;

        private string patchPath = Path.Combine("..",
            Path.Combine("..", Path.Combine("..", Path.Combine("..", Path.Combine("Graphics", "Patches")))));
        private string defaultLeftPatchName = "Patch01.jpg";
        private string defaultRightPatchName = "Patch02.jpg";
        private string soundPath = Path.Combine("..",
            Path.Combine("..", Path.Combine("..", Path.Combine("..", "Sounds"))));

        private Sound whistle;
        private Sound hit;

        public MainForm()
        {
            InitializeComponent();
            System.Threading.Thread.CurrentThread.Name = "User Interface thread";
            ActiveControl = simulationSurfaceControl;

            if (System.Environment.OSVersion.Platform == PlatformID.Win32NT)
                System.Environment.SetEnvironmentVariable("SDL_VIDEODRIVER", "directx"); 
            //log = new ElteIk.RoboSoccer.Logger.Log("log.txt");

            //ElteIk.RoboSoccer.Core.PhysicsEngine.Physics2DDotNetAdapter pengine = new ElteIk.RoboSoccer.Core.PhysicsEngine.Physics2DDotNetAdapter();
            game = new RoboSoccerGame();
            //game.StepInterval = 5;
            game.Initialize();
            renderUpdateTimer.Start();

            server = new ElteIk.RoboSoccer.Communication.MultithreadedServer(game);
            simulationSurfaceControl.BringToFront();
            simulationPanel.Visible = false;
            graphics = sdlGraphics = new ElteIk.RoboSoccer.SDL.SdlGraphicsEngine(game, simulationSurfaceControl);
            gdiGraphics = new ElteIk.RoboSoccer.Graphics.GdiGraphicsEngine(game, simulationPanel);

            //simulationPanel.Visible = false;
            //simulationPanel.BringToFront();
            //graphics = new ElteIk.RoboSoccer.Graphics.GdiGraphicsEngine(game, simulationPanel);
            game.Updated += new EventHandler(game_Updated);

            selectedRobot = game.LeftTeam.Robots[0];

            InitializeControllers();

            whistle = new Sound(Path.Combine(soundPath, "Whistle.wav"));
            hit = new Sound(Path.Combine(soundPath, "Hit.wav"));

            ofdPatch.InitialDirectory = patchPath;
            ChangeTeamPatch(1, Path.Combine(patchPath, defaultLeftPatchName));
            ChangeTeamPatch(2, Path.Combine(patchPath, defaultRightPatchName));

            // We want to see the default state at start
            graphics.Update();

            game.GoalShoot += delegate(object sndr, ElteIk.RoboSoccer.Core.SoccerGame.GoalEventArgs ea)
            {
                this.Invoke(new ControlInvoke(delegate
                {
                    whistle.Play();
                    leftScoreLabel.Text = game.LeftTeamScore.ToString();
                    rightScoreLabel.Text = game.RightTeamScore.ToString();
                    //MessageBox.Show(string.Format("Player {0} scores for team \"{1}\" .", ea.Player, ea.ScoringTeam.ToString()));
                    StartTimer();
                }));
            };

            game.RoundStart += delegate(object sndr, EventArgs ea)
            {
                whistle.Play();
                //this.Invoke(new ControlInvoke(delegate { newRoundCountDownButton.Visible = false; }));
                if (newRoundStopWatch != null)
                    newRoundStopWatch.Stop();

            };

            game.Collision += delegate(object sender, EventArgs e)
            {
                try
                {
                    hit.Play();
                }
                catch (Exception)
                {
                }
            };

            game.GameStopped += delegate(object sender, EventArgs e)
            {
                Invoke(new ControlInvoke(delegate
                {
                    startButton.Enabled = true;

                    leftControllerCombo.Enabled = true;
                    rightControllerCombo.Enabled = true;
                }));
            };

            simulationSurfaceControl.SizeChanged += delegate(object sndr, EventArgs ea)
            {
                Invoke( new ControlInvoke( delegate
                {
                    graphics.Resize();
                }));
            };

            game.SynchronizePhysics();
        }

        void InitializeControllers()
        {
            LoadTactics();
            leftControllerCombo.Items.Clear();
            leftControllerCombo.Items.Add("None");
            leftControllerCombo.Items.AddRange(GetTacticsNames());
            if (leftControllerCombo.Items.Count > 1)
                leftControllerCombo.SelectedIndex = 1;
            else
                leftControllerCombo.SelectedIndex = 0;

            rightControllerCombo.Items.Clear();
            rightControllerCombo.Items.Add("None");
            rightControllerCombo.Items.AddRange(GetTacticsNames());
            if (rightControllerCombo.Items.Count > 2)
                rightControllerCombo.SelectedIndex = 2;
            else
                rightControllerCombo.SelectedIndex = rightControllerCombo.Items.Count - 1;
        }

        private delegate void ControlInvoke();

        void game_Updated(object sender, EventArgs e)
        {
            try
            {
                Invoke(new ControlInvoke(delegate
                    {
                        TimeSpan past = TimeSpan.FromSeconds((double)game.Time);
                        timeLabel.Text = String.Format("{0:00}:{1:00}.{2:000}",
                            past.Minutes, past.Seconds, past.Milliseconds);
                    }));
            }
            catch (Exception)
            {
            }

            if (selectedRobot == null)
            {
                System.Console.WriteLine("selected: none");
                return;
            }

            KeyboardState keys = new KeyboardState();

            bool left = keys.IsKeyPressed(Key.LeftArrow);
            bool right = keys.IsKeyPressed(Key.RightArrow);

            if ( keys.IsKeyPressed(Key.A) )
            {
                System.Console.WriteLine("A");
            }

            float turningPower = 1.2f;
            if (keys.IsKeyPressed(Key.A))
            {
                if (Math.Abs(selectedRobot.MotorRight) != Math.Abs(selectedRobot.MotorLeft))
                {
                    selectedRobot.MotorRight = 0.0f;
                    selectedRobot.MotorLeft = 0.0f;
                }

                selectedRobot.MotorRight += turningPower;
                selectedRobot.MotorLeft -= turningPower;
            }

            if (keys.IsKeyPressed(Key.D))
            {
                if (Math.Abs(selectedRobot.MotorRight) != Math.Abs(selectedRobot.MotorLeft))
                {
                    selectedRobot.MotorRight = 0.0f;
                    selectedRobot.MotorLeft = 0.0f;
                }

                selectedRobot.MotorRight -= turningPower;
                selectedRobot.MotorLeft += turningPower;
            }

            if (keys.IsKeyPressed(Key.UpArrow) ||
                keys.IsKeyPressed(Key.W))
            {
                selectedRobot.MotorLeft += 1.0f;
                selectedRobot.MotorRight += 1.0f;
            }
            if (keys.IsKeyPressed(Key.DownArrow) ||
                keys.IsKeyPressed(Key.S))
            {
                selectedRobot.MotorLeft -= 1.0f;
                selectedRobot.MotorRight -= 1.0f;
            }

            if (left)
            {
                if (selectedRobot.IsBackward)
                {
                    selectedRobot.MotorLeft -= 1.0f;
                }
                else
                {
                    selectedRobot.MotorRight += 1.0f;
                }
            }

            if (right)
            {
                if (selectedRobot.IsBackward)
                {
                    selectedRobot.MotorRight -= 1.0f;
                }
                else
                {
                    selectedRobot.MotorLeft += 1.0f;
                }
            }

            if (keys.IsKeyPressed(Key.LeftControl) || keys.IsKeyPressed(Key.RightControl))
            {
                selectedRobot.MotorLeft *= 8f;
                selectedRobot.MotorRight *= 8f;
            }

            if (keys.IsKeyPressed(Key.Space))
            {
                selectedRobot.Brake = 7f;
            }

            Invoke(new ControlInvoke(delegate
            {
                if ( graphics != null )
                    graphics.Update();
            }));
        }

   		private Dictionary<string, Type> tactics = new Dictionary<string,Type>();

        /// <summary>
        /// Getting the types that implement ITactics.
        /// </summary>
        public string[] GetTacticsNames()
        {
            List<string> names = new List<string>(tactics.Count);
            foreach (string name in tactics.Keys)
                names.Add(name);

            names.Sort();

            return names.ToArray();
        }
        
        public void LoadTactics()
        {
			List<Type> foundTactics = new List<Type>(16);

            List<Assembly> assemblies = new List<Assembly>(16);
			/*Assembly currentAssembly = Assembly.GetExecutingAssembly();
			assemblies.Add(currentAssembly);

			AssemblyName[] referencedAssemblyNames = currentAssembly.GetReferencedAssemblies();

			foreach (AssemblyName assemblyName in referencedAssemblyNames)
				assemblies.Add(Assembly.Load(assemblyName));*/

            FileInfo[] dllFiles = new DirectoryInfo(".").GetFiles("*.dll");
            FileInfo[] soFiles = new DirectoryInfo(".").GetFiles("*.so");
            List<FileInfo> assemblyFiles = new List<FileInfo>();
            assemblyFiles.AddRange(dllFiles);
            assemblyFiles.AddRange(soFiles);
            foreach (FileInfo assemblyFile in assemblyFiles)
            {
                try
                {
                    Assembly assembly = Assembly.LoadFile(assemblyFile.FullName);
                    assemblies.Add(assembly);
                }
                catch (System.Exception)
                {
                }
            }

			foreach (Assembly assembly in assemblies)
			{
				foreach (Type type in assembly.GetTypes())
				{
                    if (!type.IsAbstract && typeof(ElteIk.RoboSoccer.Communication.ITactics).IsAssignableFrom(type))
				    {
                        foundTactics.Add(type);
				    }
				}
			}

            tactics.Clear();
            foreach (Type type in foundTactics)
				tactics.Add(type.Name, type);
        }

        ElteIk.RoboSoccer.TesterTactics.SimpleController ctrl;

        private void startButton_Click(object sender, EventArgs e)
        {
            startButton.Enabled = false;
            pauseButton.Enabled = true;
            resetButton.Enabled = true;

            leftControllerCombo.Enabled = false;
            rightControllerCombo.Enabled = false;

            if (leftControllerCombo.SelectedIndex > 0)
            {
                Type type = tactics[leftControllerCombo.Text];
                if (type != null)
                {
                    object[] args = {1};
                    ElteIk.RoboSoccer.Communication.ITactics leftTactics =
                        (ElteIk.RoboSoccer.Communication.ITactics)Activator.CreateInstance(type, args);
                    server.AddClient(leftTactics, 1);
                }
            }
            if (rightControllerCombo.SelectedIndex > 0)
            {
                Type type = tactics[rightControllerCombo.Text];
                if (type != null)
                {
                    object[] args = {2};
                    ElteIk.RoboSoccer.Communication.ITactics rightTactics =
                        (ElteIk.RoboSoccer.Communication.ITactics)Activator.CreateInstance(type, args);
                    server.AddClient(rightTactics, 2);
                }
            }

            whistle.Play();
            game.Start();

            RobotPlayer rp = (RobotPlayer)game.Players[0];
            selectedRobot = rp;
            ctrl = new ElteIk.RoboSoccer.TesterTactics.SimpleController(selectedRobot, game.Field);
        }

        private bool paused = false;
        private void pauseButton_Click(object sender, EventArgs e)
        {
            paused = true;
            if ( paused )
                game.Stop();
        }

        private void resetButton_Click(object sender, EventArgs e)
        {
            resetButton.Enabled = false;
            pauseButton.Enabled = false;

            game.Stop();

            //if (server.HasClient(1))
            //    server.RemoveClient(1);
            //if (server.HasClient(2))
            //    server.RemoveClient(2);

            //game = new RoboSoccerGame();
            //game.Initialize();
            //graphics.Update();
            //leftScoreLabel.Text = game.LeftTeamScore.ToString();
            //rightScoreLabel.Text = game.RightTeamScore.ToString();            
        }

        private StopWatch newRoundStopWatch;

        private void StartTimer()
        {
            //newRoundCountDownButton.Visible = true;

            newRoundStopWatch = new StopWatch(5.0);
            newRoundStopWatch.Tick += delegate(object sender, StopWatch.TickEventArgs e)
            {
                //newRoundCountDownButton.Text = string.Format("New Round (in {0:0.0} seconds)", e.RemainingTime);
            };
            newRoundStopWatch.Stopped += delegate(object sender, EventArgs e)
            {
                game.NewRound();
            };
            newRoundStopWatch.Start();
        }

        private void ChangeTeamPatch(int teamId, string filename)
        {
            try
            {
                if (File.Exists(filename))
                {
                    if ( teamId == 1 )
                        leftTeamPatchPicture.Image = new System.Drawing.Bitmap(filename);
                    else if ( teamId == 2 )
                        rightTeamPatchPicture.Image = new System.Drawing.Bitmap(filename);
                    graphics.ChangeTeamPatch(teamId, filename);
                }
            }
            catch (System.Exception e)
            {
                string s = e.ToString();
            }
        }

        private void leftLogoPicture_Click(object sender, EventArgs e)
        {
            if (ofdPatch.ShowDialog() == DialogResult.OK)
                ChangeTeamPatch(1, ofdPatch.FileName);
        }

        private void rightTeamPatchPicture_Click(object sender, EventArgs e)
        {
            if (ofdPatch.ShowDialog() == DialogResult.OK)
                ChangeTeamPatch(2, ofdPatch.FileName);
        }

        private void wireframeModeCheck_CheckedChanged(object sender, EventArgs e)
        {
            if ( !wireframeModeCheck.Checked )
            {
                simulationPanel.Visible = false;
                simulationSurfaceControl.Visible = true;
                simulationSurfaceControl.BringToFront();
                graphics = sdlGraphics; 
            }
            else
            {
                simulationPanel.Visible = true;
                simulationSurfaceControl.Visible = false;
                simulationPanel.BringToFront();
                graphics = gdiGraphics; 
            }
            graphics.Update();
        }

        private void renderUpdateTimer_Tick(object sender, EventArgs e)
        {
            if (graphics == null)
                return;
            if (graphics == sdlGraphics)
                sdlGraphics.UpdateControl();
        }
    }
}